using System.Collections;
using System.ComponentModel;
using System.Linq;

namespace System.Windows.Controls
{
    internal static class CollectionHelper
    {
        public static void Add(this IEnumerable collection, object item)
        {
            var collectionView = collection as ICollectionView;
            if (collectionView != null)
                collectionView.SourceCollection.Add(item);
            else
            {
                var count = (int) collection.GetType().GetProperty("Count").GetValue(collection, new object[0]);
                collection.Insert(count, item);
            }
        }

        public static bool CanInsert(this IEnumerable collection, object item)
        {
            var collectionView = collection as ICollectionView;
            if (collectionView != null)
                return collectionView.SourceCollection.CanInsert(item);
            if (collection.IsReadOnly())
                return false;
            var genericListType = (from interfaceType in collection.GetType().GetInterfaces()
                                   where interfaceType.FullName != null &&
                                         interfaceType.FullName.StartsWith("System.Collections.Generic.IList`1",
                                                                           StringComparison.Ordinal)
                                   select interfaceType)
                .FirstOrDefault();
            if (genericListType != null)
                return (genericListType.GetGenericArguments()[0] == item.GetType());
            return (collection is IList);
        }

        public static int Count(this IEnumerable collection)
        {
            var collectionView = collection as ICollectionView;
            if (collectionView != null)
                return collectionView.SourceCollection.Count();
            var genericListType = (from interfaceType in collection.GetType().GetInterfaces()
                                   where interfaceType.FullName != null &&
                                         interfaceType.FullName.StartsWith("System.Collections.Generic.ICollection`1",
                                                                           StringComparison.Ordinal)
                                   select interfaceType).FirstOrDefault();
            if (genericListType != null)
                return (int) genericListType.GetProperty("Count").GetValue(collection, new object[0]);
            var list = collection as IList;
            return list != null ? list.Count : collection.OfType<object>().Count<object>();
        }

        public static void Insert(this IEnumerable collection, int index, object item)
        {
            var collectionView = collection as ICollectionView;
            if (collectionView != null)
                collectionView.SourceCollection.Insert(index, item);
            else
            {
                Type genericListType = (from interfaceType in collection.GetType().GetInterfaces()
                                        where interfaceType.FullName != null &&
                                              interfaceType.FullName.StartsWith("System.Collections.Generic.IList`1",
                                                                                StringComparison.Ordinal)
                                        select interfaceType)
                    .FirstOrDefault();
                if (genericListType != null)
                    genericListType.GetMethod("Insert").Invoke(collection, new[] {index, item});
                else
                    ((IList) collection).Insert(index, item);
            }
        }

        public static bool IsReadOnly(this IEnumerable collection)
        {
            var type = collection.GetType();
            if (type.IsArray) return true;
            while(true)
            {
                type = type.BaseType;
                if (type == null) break;
                if (type.FullName != null &&
                    type.FullName
                    .StartsWith("System.Collections.ObjectModel.ReadOnlyCollection`1", StringComparison.Ordinal))
                    return true;
            }
            return false;
        }

        public static void Remove(this IEnumerable collection, object item)
        {
            var collectionView = collection as ICollectionView;
            if (collectionView != null)
                collectionView.SourceCollection.Remove(item);
            else
            {
                var genericListType = (from interfaceType in collection.GetType().GetInterfaces()
                                       where interfaceType.FullName != null &&
                                             interfaceType.FullName.StartsWith("System.Collections.Generic.IList`1",
                                                                               StringComparison.Ordinal)
                                       select interfaceType)
                    .FirstOrDefault();
                if (genericListType != null)
                {
                    var index = (int) genericListType.GetMethod("IndexOf").Invoke(collection, new[] {item});
                    if (index != -1)
                        genericListType.GetMethod("RemoveAt").Invoke(collection, new object[] {index});
                }
                else
                    ((IList) collection).Remove(item);
            }
        }

        public static void RemoveAt(this IEnumerable collection, int index)
        {
            var collectionView = collection as ICollectionView;
            if (collectionView != null)
                collectionView.SourceCollection.RemoveAt(index);
            else
            {
                var genericListType = (from interfaceType in collection.GetType().GetInterfaces()
                                       where interfaceType.FullName != null &&
                                             interfaceType.FullName.StartsWith("System.Collections.Generic.IList`1",
                                                                               StringComparison.Ordinal)
                                       select interfaceType)
                    .FirstOrDefault();
                if (genericListType != null)
                    genericListType.GetMethod("RemoveAt").Invoke(collection, new object[] {index});
                else
                    ((IList) collection).RemoveAt(index);
            }
        }

    }
}